package com.andova.egl

import android.graphics.SurfaceTexture
import android.opengl.EGL14
import android.opengl.EGLConfig
import android.opengl.EGLSurface
import android.opengl.GLES30
import android.view.TextureView
import com.andova.ext.e
import com.andova.glcamera.*

/**
 * OpenGL是单线程的，其他线程不能访问另外线程的纹理资源，但是，如果两个线程共享上下文，就可以访问彼此的纹理资源
 */
class MultiThreadEglHelper : SurfaceTexture.OnFrameAvailableListener, EglTextureCallback, OnEglThreadListener {
    private var textureView: TextureView? = null

    private var eglDisplay = EGL14.EGL_NO_DISPLAY
    private var eglDrawContext = EGL14.EGL_NO_CONTEXT
    private var eglWorkContext = EGL14.EGL_NO_CONTEXT
    private val configs = arrayOfNulls<EGLConfig>(1)
    private var eglSurface: EGLSurface? = null
    private var textureRender: OnTextureListener? = null
    private var mTextureSurface: SurfaceTexture? = null

    @Volatile
    private var mEnable = true
    private var eglTaskHandler: HT? = null
    private var eglTextureHandler: HT? = null

    fun initMultiEgl(
        textureView: TextureView, texHandler: HT?, taskHandler: HT?,
        render: OnTextureListener?, width: Int, height: Int, format: Int = DEFAULT_COLOR_FORMAT
    ) {
        mEnable = true
        this.textureView = textureView
        textureRender = render
        eglTextureHandler = texHandler?.apply {
            setListener(this@MultiThreadEglHelper)
            handleMessage(MSG_EGL_HELPER_WHAT_INIT, width, height, format)
        }

        eglTaskHandler = taskHandler?.apply { setListener(this@MultiThreadEglHelper) }
    }

    override fun onInitTexEglContext(width: Int, height: Int, format: Int, clientVersion: Int) {
        eglDisplay = EGL14.eglGetDisplay(EGL14.EGL_DEFAULT_DISPLAY)//获取显示设备
        if (eglDisplay == EGL14.EGL_NO_DISPLAY) throw RuntimeException("eglGetDisplay error: " + EGL14.eglGetError())
        val version = IntArray(2)//存放EGL版本号
        version[0] = 3
        if (!EGL14.eglInitialize(eglDisplay, version, 0, version, 1)) throw RuntimeException("eglInitialize error: " + EGL14.eglGetError())
        val attributes = when (format) {
            GLES30.GL_RGB565 -> {
                intArrayOf(
                    EGL14.EGL_BUFFER_SIZE, 16,
                    EGL14.EGL_RED_SIZE, 5,
                    EGL14.EGL_GREEN_SIZE, 6,
                    EGL14.EGL_BLUE_SIZE, 5,
                    EGL14.EGL_SURFACE_TYPE, EGL14.EGL_WINDOW_BIT,
                    EGL14.EGL_NONE
                )
            }
            GLES30.GL_RGB8, GLES30.GL_RGB -> {
                intArrayOf(
                    EGL14.EGL_BUFFER_SIZE, 24,
                    EGL14.EGL_RED_SIZE, 8,
                    EGL14.EGL_GREEN_SIZE, 8,
                    EGL14.EGL_BLUE_SIZE, 8,
                    EGL14.EGL_SURFACE_TYPE, EGL14.EGL_WINDOW_BIT,
                    EGL14.EGL_NONE
                )
            }
            GLES30.GL_RGBA -> {
                intArrayOf(
                    EGL14.EGL_BUFFER_SIZE, 32,
                    EGL14.EGL_RED_SIZE, 8,
                    EGL14.EGL_GREEN_SIZE, 8,
                    EGL14.EGL_BLUE_SIZE, 8,
                    EGL14.EGL_ALPHA_SIZE, 8,
                    EGL14.EGL_RENDERABLE_TYPE, 4,
                    EGL14.EGL_SURFACE_TYPE, EGL14.EGL_WINDOW_BIT,
                    EGL14.EGL_NONE
                )//配置列表
            }
            else -> null
        }
        val numConfigs = IntArray(1)
        //EGL选择配置
        if (!EGL14.eglChooseConfig(eglDisplay, attributes, 0, configs, 0, configs.size, numConfigs, 0)) throw RuntimeException("eglChooseConfig error: " + EGL14.eglGetError())
        val surfaceTexture = textureView?.surfaceTexture ?: throw RuntimeException("surfaceTexture is null")
        val surfaceAttributes = intArrayOf(EGL14.EGL_NONE)//创建EGL显示窗口
        eglSurface = EGL14.eglCreateWindowSurface(eglDisplay, configs[0], surfaceTexture, surfaceAttributes, 0)
        val contextAttributes = intArrayOf(EGL14.EGL_CONTEXT_CLIENT_VERSION, clientVersion, EGL14.EGL_NONE)//创建上下文环境
        eglDrawContext = EGL14.eglCreateContext(eglDisplay, configs[0], EGL14.EGL_NO_CONTEXT, contextAttributes, 0)
        if (eglDisplay == EGL14.EGL_NO_DISPLAY || eglDrawContext == EGL14.EGL_NO_CONTEXT) throw RuntimeException("eglCreateContext fail error: " + EGL14.eglGetError())
        if (!EGL14.eglMakeCurrent(eglDisplay, eglSurface, eglSurface, eglDrawContext)) throw RuntimeException("eglMakeCurrent error: " + EGL14.eglGetError())
        textureRender?.onAvailable(width, height)
        eglTaskHandler?.handleMessage(MSG_EGL_HELPER_WHAT_INIT, width, height, format)
    }

    override fun onInitTaskEglContext(width: Int, height: Int, clientVersion: Int) {
        val contextAttributes = intArrayOf(EGL14.EGL_CONTEXT_CLIENT_VERSION, clientVersion, EGL14.EGL_NONE)//创建上下文环境
        eglWorkContext = EGL14.eglCreateContext(eglDisplay, configs[0], eglDrawContext, contextAttributes, 0)
        if (!EGL14.eglMakeCurrent(eglDisplay, EGL14.EGL_NO_SURFACE, EGL14.EGL_NO_SURFACE, eglWorkContext)) throw RuntimeException("eglMakeCurrent error: " + EGL14.eglGetError())
    }

    override fun onSizeChanged(width: Int, height: Int, format: Int) {
        textureRender?.onSizeChanged(width, height)
    }

    override fun onUpdateTexImage() {
        try {
            mTextureSurface?.updateTexImage()
        } catch (e: IllegalStateException) {
            e("catch exception when onUpdateTexImage: $e")
        }
    }

    override fun onTextureFrame() {
        if (!mEnable) return
        textureRender?.run {
            onTextureFrame(mTextureSurface)
            EGL14.eglSwapBuffers(eglDisplay, eglSurface)
        }
    }

    override fun onTaskFrame() {
        if (!mEnable) return
        textureRender?.onTaskFrame(mTextureSurface)
    }

    override fun onDelayTask(what: Int) {
        textureRender?.onDelayTask(what)
    }

    fun sendForegroundMessageDelayed(what: Int, delayMillis: Long) {
        eglTextureHandler?.handleMessageDelayed(MSG_EGL_HELPER_WHAT_DELAY_TASK, what, delayMillis)
    }

    fun sendBackgroundMessageDelayed(what: Int, delayMillis: Long) {
        eglTaskHandler?.handleMessageDelayed(MSG_EGL_HELPER_WHAT_DELAY_TASK, what, delayMillis)
    }

    override fun onFrameAvailable(surfaceTexture: SurfaceTexture?) {
        if (!mEnable) return
        eglTextureHandler?.handleMessage(MSG_EGL_HELPER_WHAT_RENDER)
        eglTaskHandler?.handleMessage(MSG_EGL_HELPER_WHAT_RENDER)
    }

    override fun onLoadTexture(textureId: Int): SurfaceTexture? {
        mTextureSurface?.release()
        mTextureSurface = null
        mTextureSurface = SurfaceTexture(textureId)
        mTextureSurface?.setOnFrameAvailableListener(this)
        return mTextureSurface
    }

    override fun onResumeTexture() {
        eglTextureHandler?.handleMessage(MSG_EGL_HELPER_WHAT_RESUME)
    }

    fun onSurfaceDestroy() {
        mEnable = false
        eglTaskHandler?.removeCallbacksAndMessages()
        eglTextureHandler?.removeCallbacksAndMessages()
        mTextureSurface?.setOnFrameAvailableListener(null)

        EGL14.eglMakeCurrent(eglDisplay, EGL14.EGL_NO_SURFACE, EGL14.EGL_NO_SURFACE, EGL14.EGL_NO_CONTEXT)
        EGL14.eglDestroyContext(eglDisplay, eglDrawContext)
        EGL14.eglDestroyContext(eglDisplay, eglWorkContext)
        EGL14.eglDestroySurface(eglDisplay, eglSurface)
        EGL14.eglReleaseThread()
        EGL14.eglTerminate(eglDisplay)

        textureRender?.onDestroyed(mTextureSurface)
    }
}
